package com.abel.bigwater.data.sanchuan

import com.abel.bigwater.data.BcdString
import com.abel.bigwater.data.Bit32Array
import io.netty.buffer.ByteBuf
import org.joda.time.LocalTime
import kotlin.experimental.and

class PeriodReadDataPart(_dataLen: Int?) : SanchuanData(_dataLen) {

    /**
     * 1 间隔流量组数
    上传组数，默认 48。该值表示本次将上传多少组间隔流量，这些间隔流量平均分布在 24 小时内。
    此值与流量保存最小时间间隔（参数 0103）密切相关：
    若流量保存最小时间间隔值为 60，即表示每小时存储一次，则此处上传组数值应为 24。
    若流量保存最小时间间隔值为 30，即表示每 30 分钟存储一次，则此处上传组数值应为 48
     */
    var count: Short? = null

    /**
     * 1 间隔流量指示
    BIT0 间隔流量上报使能 0 禁止上报间隔流量 1 允许上报间隔流量
    BIT1~7 间隔流量格式（有符号）
    0 格式 0，无符号，每组 3 字节，单位 0.001L，例： 3 字节依次为 0xff,0x00,0x00 则表
    示间隔流量为 0.255L。
    1 格式 1，每组 2 字节无符号，单位 1L，例： 2 字节依次为 0xff,0x00 则表示间隔流量
    为 255L。
    2 格式 2，每组 1 字节无符号，单位 0.1 立方米，例： 0xff 表示间隔流量为 25.5 立方米
    3 格式 3，每组 2 字节有符号，单位 1L，例： 2 字节依次为 0xff,0x00 则表示间隔流量
    为 255L。
    4 格式 4，每组 1 字节有符号，单位 0.1 立方米，例： 0xff 表示间隔流量为 25.5 立方米
    其它 保留
     */
    var flag: PeriodSumFlag? = null

    /**
     * 2*N
    间隔流量 1 若间隔时间单位是 30 分钟，那么前一天 0 点 0 分至前一天 0 点 30 分内间隔流量为间隔流量 1，
    0 点 30 分至 1 点 0 分内间隔流量为间隔流量 2，依此类推第 48 组间隔流量为前一天 23 点 30 分
    至当天的 0 点 0 分间隔流量。 0x7FFF 表示没有该间隔流量。
    …
    间隔流量 N
     */
    var sumStrList: ArrayList<BcdString>? = null

    /**
     * 间隔流量
     */
    var sumList: ArrayList<PeriodSumRow>? = null
        get() {
            assert(count?.toInt() == sumStrList?.size)

            field = ArrayList()
            if ((count?.toInt() ?: 0) == 0) return field

            val period = 24 * 60 / count!!

            sumStrList.orEmpty().forEachIndexed { index, it ->
                val c1 = flag!!.format!!.unit * it.toUlongLEHex(2)!!

                field!!.add(PeriodSumRow().apply {
                    this.consumption = c1.toDouble()
                    this.startTime = LocalTime(period * index / 60, period * (index % 2))
                    this.endTime = startTime!!.plusMinutes(period)
                })
            }

            return field
        }

    /**
     * 计算数据长度，针对解析和打包
     */
    override fun computeDataLen(): Int {
        dataLen = 1 + 1 + 2 * count!!

        return dataLen!!
    }

    /**
     * 从当前位置写入打包字节流
     */
    override fun fillBuffer(buf: ByteBuf) {
        buf.alloc().buffer().apply {
            writeByte(count!!.toInt())

            writeByte(flag!!.back)

            for (idx in 0..(count!! - 1)) {
                assert(sumStrList!![idx].size == 2)

                writeBytes(sumStrList!![idx].hexArray)
            }

            // write the entire part.
            buf.writeBytes(this)
        }
    }

    /**
     * 解析字节流，从buf当前位置开始，并且消费其中的字节。
     */
    override fun <T : SanchuanData> parseBuffer(buf: ByteBuf): T? {
        buf.duplicate().apply {
            count = readByte().toShort().and(0xFF)

            flag = PeriodSumFlag(readByte().toInt())
        }
        computeDataLen()

        buf.readBytes(dataLen!!).apply {
            skipBytes(2)

            sumStrList = ArrayList()
            for (idx in 0..(count!! - 1)) {
                sumStrList!!.add(BcdString.create(this, 2))
            }
        }

        return this as T
    }
}

class PeriodSumFlag(v1: Int) : Bit32Array(v1) {
    var enabled: Boolean? = false
        get() {
            field = get(0) == 1
            return field
        }
        set(value) {
            field = value
            set(0, if (field == true) 1 else 0)
        }

    var format: PeriodSumFormat? = PeriodSumFormat.Fmt1
        get() {
            field = PeriodSumFormat.findByCode(get(1, 7))
            return field
        }
        set(value) {
            field = value
            set(1, 7, field!!.code)
        }

}

/**
 * BIT1~7 间隔流量格式（有符号）
0 格式 0，无符号，每组 3 字节，单位 0.001L，例： 3 字节依次为 0xff,0x00,0x00 则表
示间隔流量为 0.255L。
1 格式 1，每组 2 字节无符号，单位 1L，例： 2 字节依次为 0xff,0x00 则表示间隔流量
为 255L。
2 格式 2，每组 1 字节无符号，单位 0.1 立方米，例： 0xff 表示间隔流量为 25.5 立方米
3 格式 3，每组 2 字节有符号，单位 1L，例： 2 字节依次为 0xff,0x00 则表示间隔流量
为 255L。
4 格式 4，每组 1 字节有符号，单位 0.1 立方米，例： 0xff 表示间隔流量为 25.5 立方米
其它 保留
 */
enum class PeriodSumFormat(_code: Int, _len: Int, _unit: Float) {
    Fmt0(0, 3, 0.001F * 0.001F),
    Fmt1(1, 2, 0.001F),
    Fmt2(2, 1, 0.1F),
    Fmt3(3, 2, 0.001F),
    Fmt4(4, 1, 0.1F);

    val code = _code

    val len = _len

    /** in m³ */
    val unit = _unit

    companion object {
        fun findByCode(_code: Int): PeriodSumFormat? {
            return PeriodSumFormat.values().find { _code == it.code }
        }
    }
}

class PeriodSumRow {
    var startTime: LocalTime? = null
    var endTime: LocalTime? = null
    var consumption: Double? = null
}
